package com.enation.app.javashop.core.promotion.coupon.service.impl;

import com.enation.app.javashop.core.base.message.MemberRegisterMsg;
import com.enation.app.javashop.core.base.model.vo.SmsSendVO;
import com.enation.app.javashop.core.base.service.SmsManager;
import com.enation.app.javashop.core.client.system.SmsClient;
import com.enation.app.javashop.core.member.model.dos.Member;
import com.enation.app.javashop.core.member.model.dos.MemberCoupon;
import com.enation.app.javashop.core.member.service.MemberCouponManager;
import com.enation.app.javashop.core.member.service.MemberManager;
import com.enation.app.javashop.core.promotion.PromotionErrorCode;
import com.enation.app.javashop.core.promotion.coupon.model.CouponUseTypeEnum;
import com.enation.app.javashop.core.promotion.coupon.model.dos.CouponDO;
import com.enation.app.javashop.core.promotion.coupon.model.dos.CouponTypeEnum;
import com.enation.app.javashop.core.promotion.coupon.model.dos.SendCouponDTO;
import com.enation.app.javashop.core.promotion.coupon.service.CouponManager;
import com.enation.app.javashop.core.promotion.fulldiscount.model.dos.FullDiscountDO;
import com.enation.app.javashop.core.shop.service.ShopManager;
import com.enation.app.javashop.framework.context.UserContext;
import com.enation.app.javashop.framework.database.DaoSupport;
import com.enation.app.javashop.framework.database.Page;
import com.enation.app.javashop.framework.exception.NoPermissionException;
import com.enation.app.javashop.framework.exception.ServiceException;
import com.enation.app.javashop.framework.security.model.Seller;
import com.enation.app.javashop.framework.util.DateUtil;
import com.enation.app.javashop.framework.util.StringUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 优惠券业务类
 *
 * @author Snow
 * @version v2.0
 * @since v7.0.0
 * 2018-04-17 23:19:39
 */
@Service
public class CouponManagerImpl implements CouponManager {
    protected final Log logger = LogFactory.getLog(this.getClass());

    @Autowired
    @Qualifier("tradeDaoSupport")
    private DaoSupport daoSupport;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    SmsClient smsClient;
    @Autowired
    SmsManager smsManager;
    @Autowired
    private  ShopManager shopManager;
    @Autowired
    private MemberCouponManager memberCouponManager;
    @Autowired
    private MemberManager memberManager;

    @Override
    public Page list(int page, int pageSize, Long startTime, Long endTime, String keyword,String couponType) {

        Seller seller = UserContext.getSeller();
        List<Object> params = new ArrayList<>();

        String sql = "select * from es_coupon where seller_id = ? ";
        params.add(seller.getSellerId());

        if (startTime != null) {
            sql = sql.concat(" and start_time>=? ");
            params.add(startTime);
        }

        if (endTime != null) {
            sql = sql.concat(" and end_time<=? ");
            params.add(endTime);
        }

        if (StringUtil.notEmpty(keyword)) {
            sql = sql.concat(" and title like ? ");
            params.add("%" + keyword + "%");
        }

        if (StringUtil.notEmpty(couponType)) {
            sql = sql.concat(" and coupon_type = ? ");
            params.add(couponType);
        }

        sql = sql.concat(" order by coupon_id desc");

        return this.daoSupport.queryForPage(sql, page, pageSize, CouponDO.class, params.toArray());
    }

    @Override
    public List<CouponDO> getList(Integer sellerId,String couponType) {
        long now = DateUtil.getDateline();
        StringBuilder sqlBuilder = new StringBuilder("select * from es_coupon where start_time <= ? and end_time >= ? and coupon_type= ? and create_num>0");
        List<Object> term = new ArrayList<>();
        term.add(now);
        term.add(now);
        term.add(couponType);
        if(sellerId!=null){
            sqlBuilder.append(" and seller_id = ? ");
            term.add(sellerId);
        }
        sqlBuilder.append(" order by create_time desc");

        return this.daoSupport.queryForList(sqlBuilder.toString(), CouponDO.class, term.toArray());
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ServiceException.class, RuntimeException.class, Exception.class})
    public CouponDO add(CouponDO coupon) {

        this.daoSupport.insert(coupon);
        int id = this.daoSupport.getLastId("es_coupon");
        coupon.setCouponId(id);

        return coupon;
    }

    @Override
    public CouponDO edit(CouponDO coupon, Integer id) {
        this.verifyStatus(id);
        this.daoSupport.update(coupon, id);
        return coupon;
    }

    @Override
    public void delete(Integer id) {

        this.verifyStatus(id);
        this.daoSupport.delete(CouponDO.class, id);
    }

    @Override
    public void endCoupon(Integer id) {
        CouponDO couponDO = this.getModel(id);
        if (couponDO == null || !couponDO.getSellerId().equals(UserContext.getSeller().getSellerId())) {
            throw new NoPermissionException("无权操作或者数据不存在");
        }
        long dateline = DateUtil.getDateline();
        if (couponDO.getEndTime() <= dateline) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "当前优惠券活动已结束");
        }
        String sql = "update es_coupon set end_time = ? where coupon_id=?";
        this.daoSupport.execute(sql, dateline, id);
    }

    @Override
    public CouponDO getModel(Integer id) {
        return this.daoSupport.queryForObject(CouponDO.class, id);
    }

    @Override
    public void verifyAuth(Integer id) {
        CouponDO couponDO = this.getModel(id);
        if (couponDO == null || !couponDO.getSellerId().equals(UserContext.getSeller().getSellerId())) {
            throw new NoPermissionException("无权操作或者数据不存在");
        }
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void addUsedNum(Integer couponId) {
        String sql = "update es_coupon set used_num = used_num+1 where coupon_id=?";
        this.daoSupport.execute(sql, couponId);
    }


    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void addReceivedNum(Integer couponId) {
        String sql = "update es_coupon set received_num = received_num+1 where coupon_id=?";
        this.daoSupport.execute(sql, couponId);
    }

    @Override
    public Page all(int page, int pageSize, Integer sellerId,String couponType) {
        Long nowTime = DateUtil.getDateline();
        List params = new ArrayList();

        StringBuffer sql = new StringBuffer("select * from es_coupon where ? >= start_time and ? < end_time");
        params.add(nowTime);
        params.add(nowTime);

        if (sellerId != null) {
            sql.append(" and seller_id = ? ");
            params.add(sellerId);
        }

        sql.append(" order by coupon_id desc");
        Page webPage = this.daoSupport.queryForPage(sql.toString(), page, pageSize, CouponDO.class, params.toArray());

        return webPage;
    }

    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void editCouponShopName(Integer shopId, String shopName) {
        //修改优惠券的店铺名称
        String sql = "UPDATE es_coupon SET seller_name = ? where seller_id = ?";
        daoSupport.execute(sql, shopName, shopId);
    }

    @Override
    public List<CouponDO> getByStatus(Integer status) {
        Seller seller = UserContext.getSeller();
        List params = new ArrayList();

        StringBuffer sql = new StringBuffer("select * from es_coupon where seller_id = ? ");
        params.add(seller.getSellerId());

        //获取当前时间
        Long currentTime = DateUtil.getDateline();

        if (status == 1) {
            sql.append(" and end_time >= ?");
            params.add(currentTime);
        } else if (status == 2) {
            sql.append(" and end_time < ?");
            params.add(currentTime);
        }

        return this.daoSupport.queryForList(sql.toString(), CouponDO.class, params.toArray());
    }

    @Override
    public List<CouponDO> getByIds(List<Integer> couponIds) {
        if (CollectionUtils.isEmpty(couponIds)) {
            return new ArrayList<>();
        }
        List params = new ArrayList();
        StringBuffer sql = new StringBuffer("select * from es_coupon where coupon_id in ");
        sql.append(couponIds.toString().replace("[", "(").replace("]", ")"));
        List<CouponDO> couponDOS = this.daoSupport.queryForList(sql.toString(), CouponDO.class, params.toArray());
        List<CouponDO> coupons = new ArrayList<>();
        if(!CollectionUtils.isEmpty(couponDOS)){
            Map<Integer, CouponDO> couponDOMap = couponDOS.stream().collect(Collectors.toMap(CouponDO::getCouponId, Function.identity()));
            for (Integer couponId : couponIds) {
                coupons.add(couponDOMap.get(couponId));
            }
        }
        return coupons;
    }

    @Override
    public void sendCoupon(SendCouponDTO sendCouponDTO) {
        Integer pageNo = 1;
        Integer pageSize = 50;

        // 查询待发送优惠券 您的100元优惠券已到账，仅限舟山市团购商品可用，记得及时抢购哦~
        CouponDO couponDO = this.getModel(sendCouponDTO.getCouponId());

        sendByPage(pageNo,pageSize,couponDO,sendCouponDTO);

    }

    private void sendByPage(Integer pageNo, Integer pageSize,CouponDO couponDO,SendCouponDTO sendCouponDTO) {

        Page<Member> memberPage = this.daoSupport.queryForPage("SELECT member_id,mobile FROM es_member WHERE member_id not in (SELECT DISTINCT(member_id) FROM  es_order  WHERE pay_status='PAY_YES' ) and create_time>? and create_time<? ORDER BY member_id DESC", pageNo, pageSize, Member.class, sendCouponDTO.getStartTime(),sendCouponDTO.getEndTime());
        List<Member> memberList = memberPage.getData();

        //添加会员优惠券表
        List<MemberCoupon> memberCouponList = new ArrayList<>();
        for (Member member : memberList) {
            // 给会员发优惠券
            MemberCoupon memberCoupon = buildMemberCoupon(member, couponDO);
            memberCouponList.add(memberCoupon);
        }

        batchInsertMemberCounpon(memberCouponList);

        String content= "【智溢商城 优惠券】：您的"+couponDO.getTitle()+"已到账，记得及时抢购哦~";
        for (Member member : memberList) {
            // 短信通知用户
            SmsSendVO smsSendVO = new SmsSendVO();
            smsSendVO.setMobile(member.getMobile());
            smsSendVO.setContent(content);
            this.smsClient.send(smsSendVO);
        }

        if(memberPage.getData().size()==pageSize){
             sendByPage(pageNo+1,pageSize,couponDO,sendCouponDTO);
        }
    }

    private void batchInsertMemberCounpon(List<MemberCoupon> memberCouponList) {
        String sql = "INSERT INTO `es_member_coupon`(`coupon_id`, `member_id`, `create_time`,  `member_name`, `title`, `coupon_type`,  `coupon_price`, `coupon_threshold_price`, `start_time`, `end_time`, `used_status`, `seller_id`, `seller_name`) " +
                "VALUES (?, ?,  ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?);";
        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                MemberCoupon memberCoupon = memberCouponList.get(i);
                ps.setInt(1, memberCoupon.getCouponId());
                ps.setInt(2, memberCoupon.getMemberId());
                ps.setLong(3, memberCoupon.getCreateTime());
                ps.setString(4, memberCoupon.getMemberName());
                ps.setString(5, memberCoupon.getTitle());
                ps.setString(6, memberCoupon.getCouponType());
                ps.setDouble(7, memberCoupon.getCouponPrice());
                ps.setDouble(8, memberCoupon.getCouponThresholdPrice());
                ps.setLong(9, memberCoupon.getStartTime());
                ps.setLong(10, memberCoupon.getEndTime());
                ps.setInt(11, memberCoupon.getUsedStatus());
                ps.setInt(12, memberCoupon.getSellerId());
                ps.setString(13, memberCoupon.getSellerName());
            }

            @Override
            public int getBatchSize() {
                return memberCouponList.size();
            }

        });
    }

    /**
     * 验证是否可修改和删除
     * @param id 主键
     */
    private void verifyStatus(Integer id) {
        CouponDO couponDO = this.getModel(id);
        long nowTime = DateUtil.getDateline();

        //如果当前时间大于起始时间，小于终止时间，标识活动已经开始了，不可修改和删除。
        if (couponDO.getStartTime().longValue() < nowTime && couponDO.getEndTime().longValue() > nowTime) {
            //throw new ServiceException(PromotionErrorCode.E400.code(), "活动已经开始，不能进行编辑删除操作");
        }

        String sql = "select * from es_full_discount where is_send_bonus = 1 and bonus_id = ? and seller_id = ? and start_time<? and end_time>?";
        List<FullDiscountDO> list = this.daoSupport.queryForList(sql, FullDiscountDO.class, id, couponDO.getSellerId(),nowTime,nowTime);
        if (list != null && list.size() != 0) {
            String msg = "";
            for (FullDiscountDO full : list) {
                msg += "【" + full.getTitle() + "】";
            }

            throw new ServiceException(PromotionErrorCode.E400.code(), "当前优惠券参与了促销活动" + msg + "，不能进行编辑删除操作");
        }
    }

    @Override
    public void memberRegister(MemberRegisterMsg memberRegisterMsg, Integer userType,String city) {
        Member member = memberRegisterMsg.getMember();
        try{
            // 根据用户所在城市定位店铺优惠券
            Integer sellerId=null;
            Integer shopId = shopManager.getShopByCity(city);
            if(shopId!=null && shopId!=0){
                sellerId=shopId;
            }
            String usersType = CouponTypeEnum.NEW_REG.value();
            if(userType == 1){
                usersType =  CouponTypeEnum.FIRST_ORDER.value();
            }
            // 从数据库中获取优惠券
            List<CouponDO> couponList = this.getList(sellerId,usersType);

            if(CollectionUtils.isEmpty(couponList)){
                return;
            }

            // 发放优惠券
            StringBuilder contentBuilder=new StringBuilder("【智溢商城 新用户发券】：您的");
            for (CouponDO couponDO : couponList) {
                // 校验已领取总数
                if (couponDO.getReceivedNum() >= couponDO.getCreateNum()) {
                    continue;
                }
                // 发券(按照额度 领完为止) 如果限领数量为0 给他发一张券 【新人券】
                Integer limitNum = couponDO.getLimitNum();
                if(limitNum != 0){
                    for (int i = 0; i < limitNum; i++) {
                        this.memberCouponManager.receiveBonus(new MemberCoupon(member.getMemberId(),couponDO.getCouponId()));
                    }
                }else{
                    this.memberCouponManager.receiveBonus(new MemberCoupon(member.getMemberId(),couponDO.getCouponId()));
                }
                contentBuilder.append(couponDO.getTitle()+couponDO.getLimitNum()+"张,");
            }
            contentBuilder.append("已到账，记得及时抢购哦~");
        }catch (Exception e){
            logger.error("新用户注册发送优惠券",e);
        }
    }

    /**
     * 批量给会员发优惠券
     */
    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void batchPush(String[] couponIds, String content, String[] mobiles) {
        // 1.0 优惠券数组转为 list
        List<Integer> couponIdsList = new ArrayList<>();
        for (String id : couponIds) {
            couponIdsList.add(Integer.valueOf(id));
        }
        // 1.1 根据优惠券ids 查询优惠券对象
        List<CouponDO>  couponDOList= this.getByIds(couponIdsList);
        if(CollectionUtils.isEmpty(couponDOList)){
            throw new RuntimeException("没有找到对应的优惠卷!");
        }

        // 1.2 保存券剩余量不够本次发放的 优惠券名称
        List<String> insufficientCouponList = new ArrayList<>();

        // 1.3 批量发优惠券
        for (CouponDO couponDO : couponDOList) {
            // 剩余量
            Integer surplusNum = couponDO.getCreateNum() - couponDO.getReceivedNum();
            // 手机号的数量
            Integer mobileNum = mobiles.length;
            // 1.3.1 剩余量 小于 手机号的数量 将优惠券的名称记录下来
            if(surplusNum < mobileNum) {
                insufficientCouponList.add(couponDO.getTitle());
                continue;
            }

            // 1.3.2 根据手机号集合 查询会员信息
            List<Member> memberList = memberManager.getMemberByMobiles(mobiles);
            Map<Integer, Member> memberIndexMap = memberList.stream().collect(Collectors.toMap(Member::getMemberId, Function.identity(), (key1, key2) -> key2));

            // 1.3.3 添加会员优惠券表
            List<MemberCoupon> memberCouponList = new ArrayList<>();

            // 优惠券限领量
            Integer limitNum = couponDO.getLimitNum();
            // 1.4 发放优惠券
            for (Member waitSendMember : memberList) {
                // 1.4.1 优惠券限领量 为0时 是可以无限制发放
                if(limitNum >= 1){
                    // 查询当前人领券的数量是否大于等于优惠券限领的数量
                    Integer[] couponId = {couponDO.getCouponId()};
                    List<MemberCoupon> memberCouponNum = memberCouponManager.getMemberCoupon(waitSendMember.getMemberId(), couponId);
                    if(limitNum <= memberCouponNum.size()){
                        continue;
                    }
                    // 限领量 大于 10 按10张发放
                    if(limitNum > 10 ){
                        limitNum = 10;
                    }
                    // 1.4.2 给用户发优惠券
                    for (int j =0; j < limitNum; j ++ ){
                        memberCouponList.add(buildMemberCoupon(waitSendMember, couponDO));
                    }
                }else{
                    // 1.4.2 给用户发优惠券
                    memberCouponList.add(buildMemberCoupon(waitSendMember, couponDO));
                }
                // 记录数据
                if(memberCouponList.size() >= 20){
                    sendCoupon(memberCouponList, couponDO, memberIndexMap, content);
                }
            }

            // 补漏
            if(memberCouponList.size() > 0){
                sendCoupon(memberCouponList, couponDO, memberIndexMap, content);
            }
        }

        // 1.5 剩余数量不足的优惠券
        if(!CollectionUtils.isEmpty(insufficientCouponList)){
            throw new RuntimeException(insufficientCouponList +"剩余的数量不够本次批量发放!");
        }

    }

    // 发送优惠券
    private void sendCoupon(List<MemberCoupon> memberCouponList, CouponDO couponDO, Map<Integer, Member> memberIndexMap, String content){
         // 批量执行添加sql
        batchInsertMemberCounpon(memberCouponList);
        // 修改优惠卷 已领取的数量
        couponDO.setReceivedNum(couponDO.getReceivedNum() + memberCouponList.size());
        this.edit(couponDO, couponDO.getCouponId());
        // 去除已经领过的人 不给他发短信
        for (MemberCoupon memberCoupon : memberCouponList) {
            Member confirmSendMember = memberIndexMap.get(memberCoupon.getMemberId());
            // 短信通知用户
            SmsSendVO smsSendVO = new SmsSendVO();
            smsSendVO.setMobile(confirmSendMember.getMobile());
            smsSendVO.setContent(content);
            this.smsManager.sendYX(smsSendVO);
        }
        memberCouponList.clear();
    }


    /**
     * 提取重复代码(给会员发优惠券)
     */
    public MemberCoupon buildMemberCoupon(Member member, CouponDO couponDO){

        MemberCoupon memberCoupon = new MemberCoupon();
        memberCoupon.setCouponId(couponDO.getCouponId());
        memberCoupon.setTitle(couponDO.getTitle());
        memberCoupon.setCouponType(couponDO.getCouponType());
        memberCoupon.setCreateTime(DateUtil.getDateline());
        memberCoupon.setMemberId(member.getMemberId());
        memberCoupon.setMemberName(member.getUname());
        memberCoupon.setCouponPrice(couponDO.getCouponPrice());
        memberCoupon.setCouponThresholdPrice(couponDO.getCouponThresholdPrice());
        memberCoupon.setSellerId(couponDO.getSellerId());
        memberCoupon.setUsedStatus(0);
        memberCoupon.setSellerName(couponDO.getSellerName());

        // 会员优惠券使用开始时间和使用结束时间
        Long startTime=couponDO.getStartTime();
        Long endTime=couponDO.getEndTime();
        if (couponDO.getUseTimeType() != null) {
            CouponUseTypeEnum couponUseTypeEnum = CouponUseTypeEnum.valueOf(couponDO.getUseTimeType());
            switch (couponUseTypeEnum) {
                case PERIOD:
                    Integer usePeriod = couponDO.getUsePeriod();
                    startTime = DateUtil.getDateline();
                    endTime = startTime + (usePeriod+1) * 24 * 60 * 60L-1L;
                    break;
                case FIX:
                    startTime = couponDO.getUseStartTime();
                    endTime = couponDO.getUseEndTime();
                    break;
            }
        }
        //  以较大结束时间为准ThreadPoolExecutor
        endTime=(endTime.compareTo( couponDO.getEndTime())==1L)?endTime:couponDO.getEndTime();
        memberCoupon.setStartTime(startTime);
        memberCoupon.setEndTime(endTime);

        return memberCoupon;
    }

    // 给用户发优惠券
    @Override
    public void  issueVouchersToUsers(Member member,Integer couponId){
        CouponDO couponDO = this.getModel(couponId);
        Integer limitNum = couponDO.getLimitNum();
        // 发行量
        Integer createNum = couponDO.getCreateNum();
        // 已领取数量
        Integer receivedNum = couponDO.getReceivedNum();
        List<MemberCoupon> memberCouponList = new ArrayList<>();
        if(createNum >= receivedNum){
            // 一次发一张
            memberCouponList.add(buildMemberCoupon(member, couponDO));
        }
        // 批量执行添加sql
        batchInsertMemberCounpon(memberCouponList);
        // 修改优惠卷 已领取的数量
        couponDO.setReceivedNum(couponDO.getReceivedNum() + memberCouponList.size());
        this.edit(couponDO, couponDO.getCouponId());
    }

    @Override
    public List<CouponDO> getMarketingCouponsByIds(String couponIds, Integer sellerId, String couponType) {
        String sql = "select * from es_coupon where seller_id=? and coupon_type=? and coupon_id in (" + couponIds + ") and start_time <= ? and end_time >= ? and create_num>0";
        long now = DateUtil.getDateline();
        return daoSupport.queryForList(sql, CouponDO.class, sellerId, couponType, now, now);
    }
}
